# This file is part of Neuroinfo Toolkit.
#
# Neuroinfo Toolkit is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Neuroinfo Toolkit is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Neuroinfo Toolkit.  If not, see <http://www.gnu.org/licenses/>.

import re
import os
import urllib
import random
import string
from neuro.exceptions import IllegalArgumentException
from neuro.exceptions import RegularExpressionException

def colorize(string, color):
	'''
	Colorize a string ::
	
		>>> colorize("my string", "blue")
		'\x1b[34mmy string\x1b[0m'
		
	:param string: String to apply color
	:type string: str
	:param color: Color "red", "green", etc.
	:type color: str
	:returns: Colorized string
	:rtype: str
	'''
	## --- input validation	
	if(not isinstance(string, basestring)):
		raise IllegalArgumentException("String must be an instance of str")
	elif(not isinstance(color, basestring)):
		raise IllegalArgumentException("Color must be an instance of str")

	string = string.strip()
	color = color.strip().lower()

	if(string == ""):
		raise IllegalArgumentException("String cannot be empty")
	elif(color == ""):
		raise IllegalArgumentException("Color cannot be empty")
	
	if(color == "red"):
		return "\033[31m" + string + "\033[0m"
	elif(color == "yellow"):
		return "\033[33m" + string + "\033[0m"
	elif(color == "green"):
		return "\033[32m" + string + "\033[0m"
	elif(color == "blue"):
		return "\033[34m" + string + "\033[0m"
	else:
		raise IllegalArgumentException("Invalid color: " + color)

def stylize(string, style):
	'''
	Stylize a string ::
	
		>>> stylize("my string", "blink")
		'\x1b[5mmy string\x1b[0m'
		
	:param string: String to apply style
	:type string: str
	:param style: Style "blink", "bold", etc.
	:type style: str
	:returns: Stylized string
	:rtype: str
	'''
	if(not isinstance(string, basestring)):
		raise IllegalArgumentException("String must be an instance of str")
	elif(not isinstance(style, basestring)):
		raise IllegalArgumentException("Style must be an instance of str")

	string = string.strip()
	style = style.strip().lower()

	if(string == ""):
		raise IllegalArgumentException("String cannot be empty")
	elif(style == ""):
		raise IllegalArgumentException("Color cannot be empty")
		
	if(style == "blink"):
		return "\033[5m" + string + "\033[0m"
	elif(style == "bold"):
		return "\033[1m" + string + "\033[0m"
	else:
		raise IllegalArgumentException("Invalid color: " + style)

def explode(string, delimiter, strip=False, collapse=False):
	'''
	Explode string into a list ::
	
		>>> explode("my string", " ")
		["my", "string"]
		
	:param string: String to explode
	:type string: str
	:param delimiter: Single character or regular expression
	:type delimiter: str
	:param strip: Strip whitespace from cell contents *after* explosion
	:type strip: bool
	:param collapse: Collapse consecutive whitespace *before* explosion
	:type collapse: bool
	:returns: Exploded string
	:rtype: list
	'''
	## --- input validation
	if(not isinstance(string, basestring)):
		raise IllegalArgumentException("String must be instance of str")
	elif(not isinstance(delimiter, basestring)):
		raise IllegalArgumentException("Delimiter must be an instance of str")
	elif(not isinstance(strip, bool)):
		raise IllegalArgumentException("Strip must be an instance of boolean")
	elif(not isinstance(collapse, bool)):
		raise IllegalArgumentException("Collapse must be an instance of boolean")

	tmp = string.strip()

	if(tmp == ""):
		raise IllegalArgumentException("String cannot be empty")
	elif(delimiter == ""):
		raise IllegalArgumentException("Delimiter cannot be empty")

	if(collapse):
		string = re.sub("\s+", " ", string)

	list = re.split(delimiter, string)

	if(strip):
		for i in range(len(list)):
			list[i] = list[i].strip()
			
	return list

def toArray(string, row=r'[\n\r]+', col=",", strip=False, collapse=False):
	'''
	Convert a multi-line string to an array ::
	
		>>> toArray("a,b\\nc,d")
		[[a, b], [c, d]]
	
	:param string: String to convert
	:type string: str
	:param row: Row delimiter, single character or regular expression
	:type row: str
	:param col: Column delimiter, single character or regular expression
	:type col: str
	:param strip: Strip whitespace from cell contents *after* conversion
	:type strip: bool
	:param collapse: Collapse consecutive whitespace *before* explosion
	:type collapse: bool
	:returns: a list of lists
	:rtype: list
	'''
	if(not isinstance(string, basestring)):
		raise IllegalArgumentException("Input string must be an instance of str")
	elif(not isinstance(row, basestring)):
		raise IllegalArgumentException("Row delimiter must be an instance of str")
	elif(not isinstance(col, basestring)):
		raise IllegalArgumentException("Column delimiter must be an instance of str")
	elif(not isinstance(strip, bool)):
		raise IllegalArgumentException("Strip must be an instance of boolean")
	elif(not isinstance(collapse, bool)):
		raise IllegalArgumentException("Collapse must be an instance of boolean")

	tmp = string.strip()

	if(tmp == ""):
		raise IllegalArgumentException("Input string cannot be empty")
	if(row == ""):
		raise IllegalArgumentException("Row delimiter cannot be empty")
	elif(col == ""):
		raise IllegalArgumentException("Column delimiter cannot be empty")

	array = list()
	numCols = None
	rows = explode(string, row, strip=strip, collapse=collapse)

	for row in rows:
		if(row == ""):
			continue
		
		columns = explode(row, col, strip=strip, collapse=collapse)

		if(numCols and numCols != len(columns)):
			raise IllegalArgumentException("Array must be isotropic")

		array.append(columns)

		numCols = len(columns)

	return array

def regex(pattern, string):
	'''
	Perform regular expression search ::
	
		>>> regex("^(my) (string)$", "my string")
		>>> ("my", "string")
		
	:param pattern: Regular expression
	:type pattern: str
	:param string: String to apply pattern
	:type string: str
	:returns: Tuple of matches or a boolean
	:rtype: tuple, bool
	:raises: RegularExpressionException
	'''
	## --- input validation
	if(not isinstance(pattern, basestring)):
		raise IllegalArgumentException("Pattern must be a string")
	elif(not isinstance(string, basestring)):
		raise IllegalArgumentException("String must be an instance of str")

	if(pattern == ""):
		raise IllegalArgumentException("Pattern cannot be empty")
	elif(string == ""):
		raise IllegalArgumentException("String cannot be empty")
	
	try:
		regexp = re.compile(pattern, re.DOTALL)
		match = regexp.match(string)
	except Exception, e:
		raise RegularExpressionException(string, pattern, e.__str__())
	
	## --- if nothing was returned, return false
	if(match == None):
		return False
	
	groups = match.groups()

	## --- something was returned, but nothing was kept
	if(len(groups) == 0):
		return True
	
	## --- return groups
	return groups

def urlencode(string):
	'''
	URL encode a string ::
	
		>>> urlencode("Hello, World!")
		'Hello%2C+World%21'
		
	:param string: String to URL encode
	:type string: str
	:returns: URL encoded string
	:rtype: str
	'''
	if(not isinstance(string, basestring)):
		raise IllegalArgumentException("String must be an instance of str")

	string = string.strip()
	
	if(string == ""):
		raise IllegalArgumentException("String cannot be empty")
	
	## --- look at the python docs for urllib.urlencode
	map = {}
	map["x"] = string
	result = urllib.urlencode(map)[2:] 
	
	return result

def randomString(length=10):
	'''
	Create a random string of ASCII letters and numbers ::
	
		>>> randomString()
		'AHOm3Ko9yQ'

	:param length: Length of random string
	:type length: int
	:returns: Random string
	:rtype: str
	'''
	if(not isinstance(length, int)):
		raise IllegalArgumentException("String length must be an instance of int")
	elif(length < 1):
		raise IllegalArgumentException("String length must be greater than 0")

	return ''.join(random.choice(string.ascii_letters + string.digits) for x in range(length))

def removeBlankLines(string, collapse=True):
	'''
	Remove blank lines from multi-line string ::

		>>> removeBlankLines("a\\nb\\n\\nc")
		'a\\nb\\nc'
	
	:param string: String to process
	:type string: str
	:param collapse: Collapse and ignore lines with whitespace
	:type collapse: bool
	:returns: String without any blank lines
	:rtype: str
	'''
	if(not isinstance(string, basestring)):
		raise IllegalArgumentException("String parameter must be an instance of str")
	elif(not isinstance(collapse, bool)):
		raise IllegalArgumentException("Ignore must be an instance of bool")

	tmp = string.strip()

	if(tmp == ""):
		raise IllegalArgumentException("String cannot be empty")

	if(collapse):
		return os.linesep.join([s for s in string.splitlines() if(s.strip() != "")])
	else:
		return os.linesep.join([s for s in string.splitlines() if s])
